<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文本处理工具最终版</title>
    <style>
        body {
            font-family: Arial, sans-serif;
        }

        .container {
            width: 80%;
            margin: 0 auto;
        }

        textarea {
            width: 100%;
            height: 150px;
            margin-bottom: 10px;
            border-radius: 5px;
            font-family: JetBrains Mono, monospace, Consolas, sans-serif;

        }

        button {
            padding: 10px 20px;
            background-color: #007BFF;
            color: white;
            border: none;
            cursor: pointer;
            border-radius: 5px;
            margin-bottom: 13px;
        }

        .btn_process:hover {
            background-color: #0056b3;
        }

        .btn_clear {
            background-color: #7b78fa;
        }

        .btn_clear:hover {
            background-color: #c16aff;
        }

        .btn_clear2 {
            background-color: #8dc149;
        }

        .btn_clear2:hover {
            background-color: #03c078;
        }

        .btn_clear3 {
            background-color: #f44b03;
        }

        .btn_clear3:hover {
            background-color: #f8d714;
        }

        /* Toast提示样式 */
        .toast {
            visibility: hidden;
            min-width: 250px;
            margin-left: -125px;
            background-color: #ff761b;
            color: #fff;
            text-align: center;
            border-radius: 10px;
            padding: 16px;
            position: fixed;
            z-index: 1;
            left: 50%;
            bottom: 30px;
            font-size: 17px;
            opacity: 0;
            transition: opacity 0.3s, visibility 0.3s;
        }

        .toast.show {
            visibility: visible;
            opacity: 1;
        }

        #outputText {
            min-height: 600px;

        }
    </style>
</head>

<body>
    <div class="container">
        <textarea id="inputText" placeholder="请输入Markdown文本" autofocus></textarea>
        <button onclick="processTextFun()" class="btn_process">处理文本</button>
        <!-- 按钮2,清空文本 -->
        <button onclick="clearText()" class="btn_clear">清空文本</button>
        <!-- 按钮3,增添把'\['变成'['的功能 -->
        <button onclick="addBracket()" class="btn_clear2">将'\['变成'['</button>
        <!-- 中英文标点处理 -->
        <button onclick="transformChinesePunctuation()" class="btn_clear3">将中文标点转换为英文标点</button>
        <textarea id="outputText" placeholder="处理后的文本"></textarea>
    </div>
    <!-- Toast提示元素 -->
    <div id="toast" class="toast"></div>

    <script>

        const seconds = 180000;
        // // 声明全局变量存储定时器
        let refreshTimer = setTimeout(function () {
            location.reload();
        }, seconds); // 初始2分钟刷新

        document.getElementById('inputText')
            .addEventListener('paste', function (e) {
                // 阻止默认的粘贴行为
                e.preventDefault();
                // 获取剪贴板中的纯文本数据
                const text = e.clipboardData.getData('text/plain');

                // 将粘贴的文本设置为输入框的值
                document.getElementById('inputText').value = text;
                processText(text)

            });

        function processTextFun() {
            const inputText = document.getElementById('inputText').value;
            processText(inputText);

        }
        //把'\['变成'['的功能
        function addBracket() {
            const inputText = document.getElementById('inputText').value;
            //获取输出框的内容
            let outputText = document.getElementById('outputText').value;
            outputText = outputText.replace(/\\\[/g, '[');
            // 复制到剪贴板
            copyToClipboard(outputText);
            //在输出回显框显示
            document.getElementById('outputText').value = outputText;
            // 显示提示信息
            // 显示提示信息
            showToast('已将"\\\["变成"["');


        }

        //将中文标点转换为英文标点
        function transformChinesePunctuation() {
            const inputText = document.getElementById('inputText').value;
            let outputText = document.getElementById('outputText').value;
            const punctuationMap = {
                '（': '[',
                '）': ']',
                '，': ',',
                '；': ';',
                '：': ':',
                '？': '?',
                '！': '!'
            };

            outputText = outputText.replace(
                /（|）|，|；|：|？|！/g,
                match => punctuationMap[match]
            );
            document.getElementById('outputText').value = outputText;
            copyToClipboard(outputText);
            showToast('已将中文标点转换为英文标点');


        }

        function processText(text) {


            // 重置刷新计时器
            clearTimeout(refreshTimer); // 清除原有计时器
            refreshTimer = setTimeout(function () {
                location.reload();
            }, seconds); // 重新设置2分钟刷新计时器


            // 将文本按行分割成数组
            const lines = text.split('\n');
            // 存储代码块开始和结束的行索引
            const codeBlockIndices = [];
            for (let i = 0; i < lines.length; i++) {
                if (lines[i].trim().startsWith('```')) {
                    codeBlockIndices.push(i);
                }
            }
            // 存储处理后的行
            const processedLines = []; // 初始化一个空数组，用于存储处理后的行
            for (let i = 0; i < lines.length; i++) { // 遍历每一行

                const isInCodeBlock = isLineInCodeBlock(i, codeBlockIndices);

                // 如果当前行在代码块中，直接将其添加到处理后的行数组中
                if (isInCodeBlock) {
                    processedLines.push(lines[i]);
                } else {
                    // 否则，去除当前行后面的空白字符
                    let trimmedLine = lines[i].trimEnd();
                    // 如果去除空白后的行不是空字符串且不是'---'，则进行进一步处理

                    if (!trimmedLine) {
                        continue;
                    }
                    if (trimmedLine === '---') {
                        continue;
                    }

                    /**
                    //   if (
                          trimmedLine.includes('提取的文字内容') ||
                          trimmedLine.includes('文字内容提取') ||
                          trimmedLine.includes('如你还想了解') ||
                          trimmedLine.includes('要不要') ||
                          trimmedLine.includes('如果你愿意') ||
                          trimmedLine.includes('如果你需要') ||
                          trimmedLine.includes('如果你希望') ||
                          trimmedLine.includes('告诉我') ||
                          trimmedLine.includes('需要我') ||
                          trimmedLine.includes('如需我') ||
                          trimmedLine.includes('以下是图片') ||
                          trimmedLine.includes('我们来') ||
                          trimmedLine.includes('你要我') ||
                          trimmedLine.includes('如果你想') ||
                          trimmedLine.includes('是否想让我') ||
                          trimmedLine.includes('我可以') ||
                          trimmedLine.includes('你希望') ||
                          trimmedLine.includes('下面我们') ||
                          trimmedLine.includes('是否要我') ||
                          trimmedLine.includes('是否希望') ||
                          trimmedLine.includes('是否还需要') ||
                          trimmedLine.includes('是否继续') ||
                          trimmedLine.includes('好的，我')
                      ) {
                          continue;
                      } */

                    // 去除行首的井号及其后跟的空白
                    trimmedLine = trimmedLine.replace(/^#+\s+/g, '') //先去除行首的井号及其后跟的空白

                    //处理$$...$$的行内公式
                    trimmedLine = processMathInline(trimmedLine)

                    // 处理 **...** 格式的行内公式,打包下面这个成函数
                    trimmedLine = processBoldMath(trimmedLine)

                    trimmedLine=fixMarkdownBoldAndMath(trimmedLine)

                    //如果当前行开头是**，则去除开头的空格
                    if (trimmedLine.trimStart().startsWith('**')) {
                        trimmedLine = trimmedLine.trimStart();
                    }



                    //处理\*变成*的情况
                    trimmedLine = trimmedLine.replace(/\\\*/g, '*')
                    // 处理\_变成_的情况
                    trimmedLine = trimmedLine.replace(/\\\_/g, '_')
                    // trimmedLine = trimmedLine.replace(/\(\[pandas\.pydata\.org\].*?\)/g, '')

                    //处理表格单元格里面换行的情况
                    if (!trimmedLine.startsWith('|') && !trimmedLine.endsWith('|')) {
                        trimmedLine = formatBulletString(trimmedLine);
                    }

                    // 处理LaTeX公式块第二种,使用斜杠加方括号,不是使用$$的行间公式情况
                    // 获取去除前后空格的当前行内容
                    const tempLineContent = trimmedLine.trim();

                    //处理斜杠加方括号的情况
                    /* 
                     TODO:
                    飞书要求公式识别的$$必须顶格写,
                    所以遇到左缩进的且带有公式的字符串只能强行去除开头的空格,
                    但这会导致公式没有缩进,目前我找不到解决方法
                    */
                    trimmedLine = processSquareBrackets(trimmedLine,tempLineContent)

                    //处理斜杠加半圆括号的情况
                    trimmedLine = processParentheses(trimmedLine,tempLineContent)
                    

                    // 然后在您的主处理逻辑中使用
                    // 新增：处理 \text{} 内容中的下划线
                    if (trimmedLine.includes('\\text{')) {
                        trimmedLine = processTextContent(trimmedLine);
                    }


                    // 新增：处理{align*}替换为{align}
                    if (trimmedLine.includes('\\begin{align*}')
                        || trimmedLine.includes('\\end{align*}')) {
                        trimmedLine = trimmedLine.replace(/align\*/g, 'align');
                    }

                    if (trimmedLine.startsWith('|') && trimmedLine.endsWith('|')) {
                        trimmedLine = trimmedLine.replace(/\\_/g, '_');
                    }
                    // 处理表格行换行
                    const lineEnding = (trimmedLine.trim().startsWith('|')
                        && !lines[i + 1]?.trim().startsWith('|'))
                        ? '\n\n'
                        : '\n';

                    processedLines.push(trimmedLine + lineEnding);
                }
            }

            // 将处理后的行合并成字符串
            const resultText = processedLines.join('');
            // 将结果文本设置为输出框的值
            document.getElementById('outputText').value = resultText;
            // 将结果文本复制到剪贴板
            copyToClipboard(resultText);
            // 显示提示信息
            showToast('已拷贝到剪贴板');

        }


        /* 
         // 处理 **...** 格式的行内公式,打包下面这个成函数
        trimmedLine = trimmedLine.replace(/(\*\*[^*]+?\*\*)/g, (match, p1, offset, full) => {
            const prevChar = full[offset - 1] || '';
            const nextChar = full[offset + match.length] || '';

            const needSpaceBefore = !/\s/.test(prevChar);
            const needSpaceAfter = !/\s/.test(nextChar);

            return `${needSpaceBefore ? ' ' : ''}**${p1.slice(2, -2)}**${needSpaceAfter ? ' ' : ''}`;
        });
        */
        function processBoldMath(line) {
            return line.replace(/(\*\*[^*]+?\*\*)/g, (match, p1, offset, full) => {
                const prevChar = full[offset - 1] || '';
                const nextChar = full[offset + match.length] || '';

                const needSpaceBefore = !/\s/.test(prevChar);
                const needSpaceAfter = !/\s/.test(nextChar);

                return `${needSpaceBefore ? ' ' : ''}**${p1.slice(2, -2)}**${needSpaceAfter ? ' ' : ''}`;
            });
        }

        //斜杠加方括号
        function processSquareBrackets(trimmedLine,tempLineContent) {
            //斜杠加方括号
            //处理斜杠加方括号的LaTeX公式块开始/结束标记 例如：单独一行的"\["或"\]"
            if (tempLineContent === '\\[' || tempLineContent === '\\]') {
                // 转换为数学公式块标记$$
                trimmedLine = '$$';
            }
            // 处理完整的LaTeX公式块
            // 例如："\[公式内容\]"
            else if (tempLineContent.startsWith('\\[') && tempLineContent.endsWith('\\]')) {
                // 转换为Markdown数学公式块格式：
                // $$
                // 公式内容
                // $$
                trimmedLine = '$$' +      // 开始标记
                    tempLineContent.slice(2, -2) +  // 提取公式内容并去除前后空格
                    '$$';        // 结束标记
            }
            else if (tempLineContent.startsWith('\\['))
            //以'\['开头但不是以'\]'结尾的情况
            {
                trimmedLine = '$$' + tempLineContent.slice(2)
            }
            else if (tempLineContent.endsWith('\\]'))
            //以'\]'结尾但不是以'\['开头的情况
            {
                trimmedLine = tempLineContent.slice(0, -2) + '$$'
            }


            //处理引用的情况中存在\[或\]的情况
            if (tempLineContent.startsWith('>') && !tempLineContent.startsWith('>>>')) {
                if (tempLineContent.endsWith('\\[')) {
                    trimmedLine = tempLineContent.slice(0, -2) + '$$';
                }
                // 下面这种不用处理,因为上面已经处理了→以'\]'结尾但不是以'\['开头的情况
                /*  else if(trimmedLine.endsWith('\\]')) {
                     trimmedLine = lineContent.replace(/\\\]/g, ']');
                 } */
            }
            return trimmedLine;
        }


        //处理斜杠加半圆括号的情况
        function processParentheses(trimmedLine,tempLineContent) {
            //处理斜杠加半圆括号的情况
           if (tempLineContent.startsWith('\\(') && tempLineContent.endsWith('\\)')) {
                        trimmedLine = '$$' + tempLineContent.slice(2, -2) + '$$'
                    }
                    else if (tempLineContent.startsWith('\\(')) {
                        trimmedLine = '$$' + tempLineContent.slice(2)
                    }
                    else if (tempLineContent.endsWith('\\)')) {
                        trimmedLine = tempLineContent.slice(0, -2) + '$$'
                    }
            return trimmedLine;
        }


        // 处理'\text{xxx}'的内容中的下划线
        function processTextContent(line) {
            return line.replace(/\\text\{([^}]*)\}/g, function (match, content) {
                const processedContent = content.replace(/_/g, '\\_');
                return `\\text{${processedContent}}`;
            });
        }

        /**
         * 判断指定行是否在代码块内
         * @param {number} lineIndex - 当前行索引
         * @param {number[]} codeBlockIndices - 代码块索引数组
         * @returns {boolean} - 是否在代码块内
         */
        function isLineInCodeBlock(lineIndex, codeBlockIndices) {
            for (let j = 0; j < codeBlockIndices.length; j += 2) {
                if (lineIndex > codeBlockIndices[j] && lineIndex < codeBlockIndices[j + 1]) {
                    return true;
                }
            }
            return codeBlockIndices.length % 2 === 1 && lineIndex > codeBlockIndices[codeBlockIndices.length - 1];
        }


        function formatBulletString(text) {
            // 使用正则表达式替换所有 "<br>-" 为 "\n-"
            // const formattedText = text.replace(/<br>-\s*/g, '\n- ');

            // 计算 "-" 的数量
            const bulletCount = (text.match(/<br>-?/g) || []).length;

            // 如果 "-" 的数量 >= 1，返回格式化后的文本；否则返回原文本
            return bulletCount >= 1 ? text.replace(/<br>\s?-?\s*/g, '\n- ') : text;
        }

        // // 示例用法
        // const inputText = `数据中变量和观测的轴：<br>- \`0\`（默认）：每列是一个变量，每行是观测值。<br>- \`1\`：每行是一个变量，每列是观测值。<br>- \`None\`：数组被扁平化处理。`;

        // const outputText = formatBulletString(inputText);

        function processMathInline(line) {
            // debugger
            // 快速判断是否包含 $...$ 或 \(...\) 模式，否则直接返回
            if (!line.includes('$') && !line.includes('\\(') && !line.includes('\\)')) {
                return line;
            }
            const trimmed = line.trim();
            // 跳过只有 $$ 或已是 block 公式的行
            if (trimmed === '$$' || /^(\${2})(.*)(\${2})$/.test(trimmed))
                return line;

            let flagDollar = false;//表示释放出现$$..$$的情况
            // 处理原有 $...$ 格式的行内公式
            let processedLine = line.replace(/(\$[^$]+?\$)/g, (match, p1, offset, full) => {
                let prevChar = full[offset - 1] || '';
                let nextChar = full[offset + match.length] || '';

                if (prevChar === '$' && nextChar === '$') {
                    //标记出现$$..$$, 不需要替换,直接返回
                    flagDollar = true;
                    return match;
                }
                const needSpaceBefore = !/\s/.test(prevChar);
                const needSpaceAfter = !/\s/.test(nextChar);
                return `${needSpaceBefore ? ' ' : ''}$$${p1.slice(1, -1)}$$${needSpaceAfter ? ' ' : ''}`;
            });
            if (flagDollar) {
                //下面可以算作查漏补缺,处理$$..$$的情况
                processedLine = processedLine.replace(/(\$\$[^$]+?\$\$)/g, (match, p1, offset, full) => {
                    let prevChar = full[offset - 1] || '';
                    let nextChar = full[offset + match.length] || '';
                    /*
                    要排除原本就有  $$..$$ 的情况,此时prevChar为'$'且nextChar为'$'
                    例如：
                    原文本：
                    你好，$$x^2+y^2=z^2$$，很高兴见到你。
                    处理后：
                    你好，[空格]$$x^2+y^2=z^2$$[空格]，很高兴见到你。
                    也就是只需加空格
                    */
                    const needSpaceBefore = !/\s/.test(prevChar);
                    const needSpaceAfter = !/\s/.test(nextChar);

                    return `${needSpaceBefore ? ' ' : ''}${p1}${needSpaceAfter ? ' ' : ''}`;
                });

            }


            // 处理 \(...\) 格式的行内公式
            processedLine = processedLine.replace(/\\\((.*?)\\\)/g, (match, p1, offset, full) => {
                const prevChar = full[offset - 1] || '';
                const nextChar = full[offset + match.length] || '';

                const needSpaceBefore = !/\s/.test(prevChar);
                const needSpaceAfter = !/\s/.test(nextChar);

                return `${needSpaceBefore ? ' ' : ''}$$${p1}$$${needSpaceAfter ? ' ' : ''}`;
            });



            return processedLine;
            /* 
            
            | 参数名| 含义| 示例（你的代码）|
            |-----------|----------------------------------------------------------------------|---------------------------------------------------------------------------------|
            | `match`| **完整匹配的子串**（即正则匹配到的整个内容）| 如果匹配 ` $$bar{x}$$ `，则 `match = " $$bar{x}$$ "`|
            | `p1`| **第 1 个捕获组**（正则中第一个 `()` 匹配的内容）| 正则 `/(\ $$[^$$ ]+?\ $$)/` 中 `p1` 就是 `$$ bar{x}$`（和 `match` 相同，因为整个被捕获）|
            | `offset`| **匹配到的子串在原字符串中的偏移量**（索引位置）| 若 ` $$bar{x}$$ ` 从第 5 个字符开始，则 `offset = 5`|
            | `full`| **原字符串**（即调用 `replace` 的原始字符串）| `full = input`（你的完整输入字符串）|
            | （未使用）| `p2, p3...`：其他捕获组（你的正则只有一个捕获组，所以没有）| -|
            | （未使用）| `groups`：命名捕获组对象（你的正则未命名捕获组）| -|
            */

            /*
            // 处理数学公式标记使用$且为行内不是行间公式的情况
            if (trimmedLine.trim() !== '$$' && trimmedLine.includes('$')) {
                // 统一处理四种情况：前后有/无空格
                /~  trimmedLine = trimmedLine
                        .replace(/(\s)\$([\w\\=+\-~/^_(){}, ]+)\$(\s)/g, '$1$$$$$2$$$$$3')       // 前后有空格
                        .replace(/([^\s\$])\$([\w\\=+\-~/^_(){}, ]+)\$([^\s\$])/g, '$1 $$$$$2$$$$ $3')  // 前后无空格
                        .replace(/(\s)\$([\w\\=+\-~/^_(){}, ]+)\$([^\s\$])/g, '$1$$$$$2$$$$ $3')  // 仅前面有空格
                        .replace(/([^\s\$])\$([\w\\=+\-~/^_(){}, ]+)\$(\s)/g, '$1 $$$$$2$$$$$3'); // 仅后面有空格
                        ~/
                trimmedLine = trimmedLine.replace(/(\$[^$]+?\$)/g, (match, p1, offset, full) => {
                    const prevChar = full[offset - 1] || '';
                    const nextChar = full[offset + match.length] || '';

                    // 判断是否需要在前后添加空格
                    const needSpaceBefore = !/\s/.test(prevChar);
                    const needSpaceAfter = !/\s/.test(nextChar);

                    const result = `${needSpaceBefore ? ' ' : ''}$$${p1.slice(1, -1)}$$${needSpaceAfter ? ' ' : ''}`;
                    return result;
                });
            }
            */
        }

        /**
         * 修复 Markdown 文本中加粗与数学公式混合时的格式问题
         * @param {string} text - 原始 Markdown 文本
         * @returns {string} 修复后的 Markdown 文本
         */
        function fixMarkdownBoldAndMath(text) {
            // 先匹配是否存在** $$或$$ **
            //一个或多个空格->正则表达式\s+
            if (!/(\*\*\s+\$\$|\$\$\s+\*\*)/.test(text)) {
                return text;
            }
            /* 输入: "左侧错误 ** $$公式$$ 1** 右侧正常"
          预期: "左侧错误 $$公式$$ **1** 右侧正常" */
            // 2. 对一个字符串,先从左到右依次找左侧错误模式  ** $$...$$ `不是$*空格的字符`**
            const leftPattern = /\s?\*\*(\s+\$\$[^$]+?\$\$\s)([^\$*\s]+?)\*\*/g;
            // 3. 对每个匹配项,将其替换为 $$...$$ **`不是$*空格的字符`**
            text = text.replace(leftPattern, (match, p1, p2) => {
                //先去除最左侧的**,最右侧**不要去除
                return p1 + "**" + p2 + "**";
            });

            /* 输入: "左侧正常 **1** $$公式$$ **右侧错误"
            预期: "左侧正常 **1** $$公式$$ 右侧错误" */
            // 4. 对一个字符串,从左到右依次找右侧错误模式  ** $$...$$ `不是$*空格的字符`**
            const rightPattern = /\*\*([^\$*\s]+?)(\s+\$\$[^$]+?\$\$\s+)\*\*\s?/g;
            // 5. 对每个匹配项,将其替换为 $$...$$ **`不是$*空格的字符`**
            text = text.replace(rightPattern, (match, p1, p2) => {
                //先去除最右侧的**,最左侧**不要去除
                return "**" + p1 + "**" + p2;
            });

            return text;
        }
        function copyToClipboard(text) {
            // 创建临时textarea元素
            const tempTextarea = document.createElement('textarea');
            tempTextarea.value = text;
            document.body.appendChild(tempTextarea);

            // 选中textarea中的文本
            tempTextarea.select();
            tempTextarea.setSelectionRange(0, 99999); // 兼容移动端

            // 执行拷贝
            document.execCommand('copy');

            // 移除临时textarea元素
            document.body.removeChild(tempTextarea);
        }
        function showToast(text = '已拷贝到剪贴板') {
            const toast = document.getElementById('toast');
            toast.innerText = text;
            toast.classList.add('show');
            setTimeout(() => {
                toast.classList.remove('show');
            }, 1500);
        }
        function clearText() {
            document.getElementById('inputText').value = '';
            document.getElementById('outputText').value = '';
            document.getElementById('inputText').focus();
        }


    </script>
</body>

</html>